package com.gitee.wsl.scheduler.job.runtime

import com.gitee.wsl.scheduler.job.KJob
import com.gitee.wsl.scheduler.job.bean.BaseJob
import com.gitee.wsl.scheduler.job.bean.JobExecutionType
import com.gitee.wsl.logger.error
import com.gitee.wsl.scheduler.job.context.CompletionJobContext
import com.gitee.wsl.scheduler.job.context.ErrorJobContext
import com.gitee.wsl.scheduler.job.context.JobContext
import com.gitee.wsl.scheduler.job.context.JobRegisterContext
import com.gitee.wsl.scheduler.job.dsl.KJobFunctions

interface RunnableJob {

    val job: BaseJob

    val executionType: JobExecutionType

    val maxRetries: Int

    suspend fun execute(context: JobContext<*>): JobResult

    companion object
}


fun RunnableJob.Companion.create(
    job: BaseJob,
    configuration: KJob.Configuration,
    block: JobRegisterContext<BaseJob, JobContext<BaseJob>>.(BaseJob) -> KJobFunctions<BaseJob, JobContext<BaseJob>>
): RunnableJob = DefaultRunnableJob(job, configuration, block)


class DefaultRunnableJob<J : BaseJob>(
    override val job: J,
    configuration: KJob.Configuration,
    block: JobRegisterContext<J, JobContext<J>>.(J) -> KJobFunctions<J, JobContext<J>>
) : RunnableJob {
    private val rjc = JobRegisterContext<J, JobContext<J>>(configuration)
    private val sjc = block(rjc, job)

    override val executionType: JobExecutionType = rjc.executionType
    override val maxRetries: Int = rjc.maxRetries

    @Suppress("UNCHECKED_CAST")
    override suspend fun execute(context: JobContext<*>): JobResult {
        return try {
            if (!context.start()) error("Failed to start execution.")
            sjc.executeFn(context as JobContext<J>)
            if (!context.complete()) error("Failed to complete execution.")
            sjc.completeFn(CompletionJobContext(context.scheduledJob()))
            JobSuccessful
        } catch (t: Throwable) {
            context.logger.error("Execution failed", t)
            try {
                sjc.errorFn(ErrorJobContext(context.scheduledJob(), t))
            } catch (t: Throwable) {
                context.logger.error("Error handler also failed", t)
            }
            JobError(t)
        }

    }
}


